#include "Enemy.h"
#include "Game.h"
#include "AnimationSystem.h"
#include "GameplayState.h"
#include "TileSystem.h"
#include "Player.h"
#include "DestroyGameObjectMessage.h"
#include "Augment.h"
#include "CreatePickupHealthMessage.h"
#include "CreatePickupShieldMessage.h"
#include "GiantSpider.h"

Enemy::Enemy()
{
}

Enemy::~Enemy()
{
	if (m_aAugment) delete m_aAugment;
}

void Enemy::Update(float elapsedTime)
{
	pastPosition = m_ptPosition;
	updating = false;

	GameplayState *gameplay = GameplayState::GetInstance();
	TileSystem *TS = TileSystem::GetInstance();

	if (TS->CheckCollison(gameplay->GetMapIndex(), SGD::Point((m_ptPosition.x + 8) * facing, m_ptPosition.y + 8)))
		m_vtVelocity.x = -m_vtVelocity.x;
	
	
	float distToPlayer = (gameplay->GetPlayer()->GetPosition() - m_ptPosition).ComputeLength();
	SGD::Point cam = Game::GetInstance()->GetCameraPos();

	// ignore update if out of view
	if (distToPlayer > Game::GetInstance()->GetScreenWidth())
		return;

	// keep on the screen?
	if ((GetRect().bottom < cam.y || GetRect().top > cam.y + Game::GetInstance()->GetScreenHeight()) && gravityOn == false)
		return;

	// passed return checks, turn upadting back on
	updating = true;

	bool rightFoot = TS->CheckCollison(gameplay->GetMapIndex(), { GetRect().right, GetPosition().y + 5 });
	bool leftFoot = TS->CheckCollison(gameplay->GetMapIndex(), { GetRect().left, GetPosition().y + 5 });

	// change between states
	// if enemy is Dead
	if (m_iHealth <= 0.0f)
		EnterDead();
	// if jumping
	else if (!rightFoot && !leftFoot && useGravity)
	{
		//GiantSpider* boss = dynamic_cast<GiantSpider*>(this);
		//if (boss && boss->GetOnGround())
			EnterJumping();
	}
	// within 1/3 screen width of player?
	else if (DoAttack())
		EnterCloseIn();
	else
		EnterPatrol();

	switch (state)
	{
	default: //(IDLE)
		break;
	case PATROL:
		Patrol();
		break;
	case JUMPING:
		m_vtVelocity += gravity * elapsedTime;
		break;
	case CLOSEIN:
		Attack();
		break;
	case ATTACK:
		Attack();
		break;
	case DEAD:
		{
			 DestroyGameObjectMessage * Msg = new DestroyGameObjectMessage{ this };
			 Msg->QueueMessage();
			 Msg = nullptr;
			 SetIsAlive(false);
			 break;
		}
	};

	MovingObject::Update(elapsedTime);
}

void Enemy::Render()
{
	SGD::Point cam = Game::GetInstance()->GetCameraPos();
	float width = Game::GetInstance()->GetScreenWidth();
	float height = Game::GetInstance()->GetScreenHeight();
	if (m_ptPosition.x < cam.x || m_ptPosition.x > cam.x + width) return;
	if (m_ptPosition.y < cam.y || m_ptPosition.y > cam.y + height) return;

	AnimationSystem::GetInstance()->Render({ GetPosition().x - Game::GetInstance()->GetCameraPos().x, GetPosition().y - Game::GetInstance()->GetCameraPos().y }, animInfo);
	if (m_fDotTimer > 0)
	{
		myEmitter->Render();
	}

	if (m_fSlowTimer > 0)
	{
		myEmitter->Render();

	}

	if (m_fStunTimer > 0)
	{
		myEmitter->Render();
	}
}

void Enemy::StartAnimation(char* anim)
{
	SetCurrAnimation(anim);
	AnimationSystem::GetInstance()->StartPlaying(animInfo);
	AnimationSystem::GetInstance()->StartLooping(animInfo);
}

void Enemy::EnterIdle()
{
	if (state == State::IDLE) return;

	state = State::IDLE;
}

/*virtual*/ void Enemy::EnterPatrol()
{
	if (state == State::PATROL) return;

	if (state == State::JUMPING)
	{
		state = prevState;
	}
	else state = State::PATROL;
}

/*virtual*/ void Enemy::EnterJumping()
{
	if (state == State::JUMPING) return;

	prevState = state;
	state = State::JUMPING;
}

/*virtual*/ void Enemy::EnterCloseIn()
{
	if (state == State::CLOSEIN) return;

	if (state == State::JUMPING)
	{
		state = prevState;
	}
	else state = State::CLOSEIN;
}

/*virtual*/ void Enemy::EnterAttack()
{
	if (state == State::ATTACK) return;

	if (state == State::JUMPING)
	{
		state = prevState;
	}
	else state = State::ATTACK;
}

/*virtual*/ void Enemy::EnterDead()
{
	if (state == State::DEAD) return;

#pragma region scoring system
	if (GetEnemyType() == EnemyType::ENEMY_BOSS)
		GameplayState::GetInstance()->GetPlayer()->ModifyScore(1000);
	if (GetEnemyType() == EnemyType::ENEMY_ARCHER)
		GameplayState::GetInstance()->GetPlayer()->ModifyScore(100);
	if (GetEnemyType() == EnemyType::ENEMY_BAT)
		GameplayState::GetInstance()->GetPlayer()->ModifyScore(25);
	if (GetEnemyType() == EnemyType::ENEMY_ELF)
		GameplayState::GetInstance()->GetPlayer()->ModifyScore(100);
	if (GetEnemyType() == EnemyType::ENEMY_GOBLIN)
		GameplayState::GetInstance()->GetPlayer()->ModifyScore(100);
	if (GetEnemyType() == EnemyType::ENEMY_KNIGHT)
		GameplayState::GetInstance()->GetPlayer()->ModifyScore(100);
	if (GetEnemyType() == EnemyType::ENEMY_SPIDER)
		GameplayState::GetInstance()->GetPlayer()->ModifyScore(25);
#pragma endregion

#pragma region item drops
	// odds of dropping something
	int drop = rand() % 100;
	// 75% chance for no drop
	if (drop < 75)
	{

	}
	// 15% chance to drop a shield
	else if (drop > 75 && drop < 90)
	{
		CreatePickupShieldMessage* Msg = new CreatePickupShieldMessage(m_ptPosition.x, m_ptPosition.y);
		Msg->QueueMessage();
		Msg = nullptr;
	}
	// 10% chance to drop a healthpack
	else if (drop > 90 && drop < 100)
	{
		CreatePickupHealthMessage* Msg = new CreatePickupHealthMessage(m_ptPosition.x, m_ptPosition.y);
		Msg->QueueMessage();
		Msg = nullptr;
	}
#pragma endregion

	state = State::DEAD;
}

/*virtual*/ bool Enemy::DoAttack()
{
	GameplayState *gameplay = GameplayState::GetInstance();
	float distanceFromPlayer = (gameplay->GetPlayer()->GetPosition() - m_ptPosition).ComputeLength();

	return (distanceFromPlayer < Game::GetInstance()->GetScreenWidth() / 3);
}

/*virtual*/ void Enemy::Attack()
{

}

/*virtual*/ void Enemy::Patrol()
{
	GameplayState *gameplay = GameplayState::GetInstance();
	TileSystem *TS = TileSystem::GetInstance();

	bool rightFoot = TS->CheckCollison(gameplay->GetMapIndex(), { GetRect().right + 5, GetPosition().y + 5 });
	bool leftFoot = TS->CheckCollison(gameplay->GetMapIndex(), { GetRect().left - 5, GetPosition().y + 5 });
	bool rightWall = TS->CheckCollison(gameplay->GetMapIndex(), { GetRect().right + 10, GetPosition().y });
	bool leftWall = TS->CheckCollison(gameplay->GetMapIndex(), { GetRect().left - 10, GetPosition().y });

	//if (leftWall || (useGravity && !leftFoot) || m_ptPosition.x < 5)
	//	MoveRight();
	//else if (rightWall || (useGravity && !rightFoot))
	//	MoveLeft();

	if (patrolDirectionRight)
	{
		if (rightWall || (useGravity && !rightFoot))
			patrolDirectionRight = false;
		else MoveRight();
	}
	else
	{
		if (leftWall || (useGravity && !leftFoot) || m_ptPosition.x < 5)
			patrolDirectionRight = true;
		else MoveLeft();
	}
}

/*virtual*/ void Enemy::MoveLeft(float speed)
{
	m_vtVelocity = { -speed, m_vtVelocity.y };

	if (GetEnemyType() == ENEMY_BOSS && GameplayState::GetInstance()->GetCurrLevel() == 2)
		SetFacing(GameplayState::GetInstance()->GetPlayer()->GetPosition().x < m_ptPosition.x ? -1 : 1); // face the player not direction
	else
		SetFacing(-1);
}

/*virtual*/ void Enemy::MoveRight(float speed)
{
	m_vtVelocity = { speed, m_vtVelocity.y };

	if (GetEnemyType() == ENEMY_BOSS && GameplayState::GetInstance()->GetCurrLevel() == 2)
		SetFacing(GameplayState::GetInstance()->GetPlayer()->GetPosition().x < m_ptPosition.x ? -1 : 1); // face the player not direction
	else
		SetFacing(1);
}

float Enemy::RandPatrolDistance()
{
	int dist = rand() % 400 - 200;

	return (float)dist;
}

float Enemy::RotationToPlayer()
{
	SGD::Point playerPos = GameplayState::GetInstance()->GetPlayer()->GetPosition();

	SGD::Vector orientation = { 0.0f, -1.0f };
	SGD::Vector toPlayer = SGD::Point(playerPos.x, playerPos.y - 15) - GetWeaponOffset();
	float angleToPlayer = orientation.ComputeAngle(toPlayer);

	if (orientation.ComputeSteering(toPlayer) < 0.0f)
		angleToPlayer = -angleToPlayer;

	return angleToPlayer;
}

void Enemy::CharacterCollision(GameObject* pOther)
{
	// CHARACTER COLLISION
	GameplayState* gameplay = GameplayState::GetInstance();

	// create rect for pastPosition
	if (animInfo.GetCurrAnimation() != "" && AnimationSystem::GetInstance()->Contains(animInfo.GetCurrAnimation()))
	{
		//shortcuts
		SGD::Rectangle colRect = pastCollisionRect;

		//Return the current collision rectangle
		pastRect = { SGD::Point(pastPosition.x - pastAnchorPoint.x + colRect.left, pastPosition.y - pastAnchorPoint.y + colRect.top),
			SGD::Size(colRect.ComputeWidth(), colRect.ComputeHeight()) };
	}

	// create rect for pOther->Location
	SGD::Rectangle otherRect;
	if (animInfo.GetCurrAnimation() != "" && AnimationSystem::GetInstance()->Contains(animInfo.GetCurrAnimation()))
	{
		//shortcuts
		Frame currFrame = AnimationSystem::GetInstance()->GetLoaded()[animInfo.GetCurrAnimation()].GetFrames()[animInfo.GetCurrFrame()];
		SGD::Rectangle colRect = currFrame.GetCollisionRect();
		SGD::Point pt = currFrame.GetAnchorPoint();

		//Return the current collision rectangle
		otherRect = { SGD::Point(pOther->GetPosition().x - pt.x + colRect.left, pOther->GetPosition().y - pt.y + colRect.top),
			SGD::Size(colRect.ComputeWidth(), colRect.ComputeHeight()) };
	}

	// check CharacterCollision, if true fix position: works on any character type
	if (gameplay->CheckCharacterCollision(pastRect, GetRect(), otherRect))
	{
		// reset position based on side of collision
		if (gameplay->left || gameplay->right)
			m_ptPosition.x = pastPosition.x;
		if (gameplay->top)
		{
			m_vtVelocity = { -200, -m_vtVelocity.y };
			m_ptPosition += m_vtVelocity * dt;
		}
	}
}
